#include "Ppoller.h"
#include "Eventloop.h"
#include "IOchannelProxy.h"
#include "log/Logger.h"
#include "utility/assertion.h"
#include "utility/cast.h"
#include "utility/feature.h"
#include "wrap/poll.h"

#include <poll.h> // for struct poll_fd


namespace _ {

static int toggle_valid(int fd) {
    return -fd - 1;
}

static int make_valid(int fd) {
    return fd < 0 ? toggle_valid(fd) : fd;
}

}

Ppoller::Ppoller(const Eventloop &loop):
    Poller{loop},
    _fdMap{},
    _pollfds{} {}

Ppoller::~Ppoller() = default;

bool Ppoller::has(const IOchannelProxy &channel) const {
    return _is_in_channels(channel.fd()) &&
            _fdMap.at(channel.fd()).channel.value().get() == channel;
}

Utility::timepoint Ppoller::poll(int timeout , IOchannelProxyRefs &activeChannels) {
    return Poller::_polling_nfds(
        Wrap::poll_(_pollfds.data(), _pollfds.size(), timeout), activeChannels);
}

void Ppoller::remove(IOchannelProxy &channel) {
    _loop.ASSERT_IN_THIS_THREAD();
    LOG_VERBOSE << "fd = " << channel.fd();
    assert(has(channel));
    assert(channel.is_idling());
    _swap_back(channel);
    MAYBE_UNUSED const auto n = _fdMap.erase(channel.fd());
    assert(n == 1);
    _pollfds.pop_back();
}

void Ppoller::update(IOchannelProxy &channel) {
    _loop.ASSERT_IN_THIS_THREAD();
    LOG_VERBOSE << "fd = " << channel.fd() 
                << " events = " << channel.listen_events_string();
    if (_fdMap.find(channel.fd()) != _fdMap.end()) {
        _change(channel);
        return;
    }
    _add(channel);
}

void Ppoller::_add(IOchannelProxy &channel) {
    assert(!_is_in_channels(channel.fd()));
    struct pollfd pfd {};
    pfd.fd = channel.fd();
    Cast::assign(pfd.events, channel.listen_events());
    pfd.revents = 0;
    _pollfds.emplace_back(pfd);
    MAYBE_UNUSED const auto inserted = 
        _fdMap.insert({channel.fd(), _Helper{channel, _pollfds.size() - 1}}).second;
    assert(inserted);
}

void Ppoller::_change(const IOchannelProxy &channel) {
    assert(has(channel));
    const auto index = _fdMap.at(channel.fd()).index;
    assert(index < _pollfds.size());
    assert(_pollfds.at(index).fd == _::make_valid(channel.fd()));
    _pollfds.at(index).fd = 
        channel.is_idling() ? _::toggle_valid(channel.fd()) : channel.fd();
    Cast::assign(_pollfds.at(index).events, channel.listen_events());
    _pollfds.at(index).revents = 0;
}

void Ppoller::_fill_active_channels(int numEvents, IOchannelProxyRefs &activeChannels) const {
    auto events = numEvents;
    for (auto pfd = _pollfds.begin(); pfd != _pollfds.end() && events > 0; ++pfd) {
        if (pfd -> revents <= 0) {
            continue;
        }
        --events;
        assert(_is_in_channels(pfd->fd));
        const auto &channel = _fdMap.at(pfd->fd).channel.value();
        assert(channel.get().fd() == pfd->fd);
        channel.get().set_return_events(pfd->revents);
        activeChannels.emplace_back(channel);
    }
}

bool Ppoller::_is_in_channels(int fd) const {
    const auto iter = _fdMap.find(fd);
    return iter != _fdMap.cend() && iter->second.channel.has_value();
}

void Ppoller::_swap_back(const IOchannelProxy &channel) {
    const auto index = _fdMap.at(channel.fd()).index;
    assert(index < _pollfds.size());
    assert(_pollfds.at(index).fd == _::toggle_valid(channel.fd()));
    assert(_pollfds.at(index).events == channel.listen_events());
    if (index != _pollfds.size() - 1) {
        _fdMap.at(_::make_valid(_pollfds.back().fd)).index = index;
        std::swap(_pollfds.at(index), _pollfds.back());
    }
}






